<?php

if (! defined('__TCPCLIENT_PHP_INCLUDED__')) {
define('__TCPCLIENT_PHP_INCLUDED__', 1);

require_once(dirname(__FILE__).'/CMLibTcpSocket.php');
require_once(dirname(__FILE__).'/NetUtil.php');

interface PackageSplitter
{
	public function getHeaderSize();
	public function parsePackageSize($header);
}

function microtime_float()
{
    list($usec, $sec) = explode(" ", microtime());
    return ((float)$usec + (float)$sec);
}

class TcpClientShort
{
	private $host = "127.0.0.1";
	private $port = 9000;
	private $unix = false;
	private $_callTimeout = 0.1;	//second
	private $_connTimeout = 0.01;	//usecond
	private $_error_code = 0;
	private $_error;
	private $_splitter;

	function __construct($splitter) {
		$this->_splitter = $splitter;
	}

	public function getLastErrorCode() {
		return $this->_error_code;
	}

	public function getLastError() {
		return $this->_error;
	}

	function setServer($host, $port, $unix = false) {
		$this->host = $host;
		$this->port = $port;
		$this->unix = $unix;
	}

	function setTimeout($conn_timeout = 0.01, $call_timeout = 0.1) {
		if($call_timeout < $conn_timeout) {
			$call_timeout = $conn_timeout;
		}
		$this->_connTimeout = $conn_timeout;
		$this->_callTimeout = $call_timeout;
	}
	
	function send($packet) {
		$sock = new CMLibTcpSocket();
		$timeout = new CMLibTimeout($this->_callTimeout);
		$conn_ret = $sock->connect($this->host, $this->port, $this->_connTimeout);
		if (false === $conn_ret) {
			$this->_error = 'connect timeout'.
					"#code:".strval($sock->getLastError()).
					"#info:".$sock->getLastErrorMsg();
			$sock->close();
			return false;
		}

		$len = $sock->write($packet, strlen($packet), $timeout);
		if (false === $len) {
			$this->_error = 'write error'.
					"#code:".strval($sock->getLastError()).
					"#info:".$sock->getLastErrorMsg();
			$sock->close();
			return false;
		}

		return true;
	}

	function sendReceive($packet,$count=1) {
		$domain = ($this->unix) ? (AF_UNIX) : (AF_INET);
		$protocol = ($this->unix) ? (0) : (SOL_TCP);
		$sock = new CMLibTcpSocket($domain, $protocol);

		$timeout = new CMLibTimeout($this->_callTimeout);
		$conn_ret = $sock->connect($this->host, $this->port, $this->_connTimeout);
		if ($conn_ret === false) {
			$this->_error_code = $sock->getLastError();
			$this->_error = 'connect error'.
					"#code:".strval($sock->getLastError()).
					"#info:".$sock->getLastErrorMsg();
			$sock->close();
			$sock = null;
			return false;
		}

		while($count-->0) {
		$time_start = microtime_float();
		$len = $sock->write($packet, strlen($packet), $timeout);
		if ($len === false) {
			$this->_error_code = $sock->getLastError();
			$this->_error = 'write error'.
					"#code:".strval($sock->getLastError()).
					"#info:".$sock->getLastErrorMsg();
			$sock->close();
			$sock = null;
			return false;
		}

		$header_len = $this->_splitter->getHeaderSize();
		$header_pack = $sock->read($header_len, $timeout);
		if ($header_pack === false) {
			$this->_error_code = $sock->getLastError();
			$this->_error = 'read header error 0'.
					"#code:".strval($sock->getLastError()).
					"#info:".$sock->getLastErrorMsg();
			$sock->close();
			$sock = null;
			return false;
		}

		$time_end = microtime_float();
		$totaltime += ($time_end-$time_start);	
		if ($sock->getLastError() != 0) {
			$this->_error_code = $sock->getLastError();
			$this->_error = 'read header error 1'.
					"#code:".strval($sock->getLastError()).
					"#info:".$sock->getLastErrorMsg();
			$sock->close();
			$sock = null;
			return false;
		}

		if (strlen($header_pack) == 0) {
			$this->_error_code = $sock->getLastError();
			$this->_error = 'read header error 2, server close'.
					"#code:".strval($sock->getLastError()).
					"#info:".$sock->getLastErrorMsg();
			$sock->close();
			$sock = null;
			return false;
		}

		$len = $this->_splitter->parsePackageSize($header_pack);
		if ($len === false) {
			$this->_error_code = $sock->getLastError();
			$this->_error = 'parse head error'.
					"#code:".strval($sock->getLastError()).
					"#info:".$sock->getLastErrorMsg();
			$sock->close();
			$sock = null;
			return false;
		}

		$body = $sock->read($len - $header_len, $timeout);
		if ($body === false) {
			$this->_error_code = $sock->getLastError();
			$this->_error = 'read content error 0'.
					"#code:".strval($sock->getLastError()).
					"#info:".$sock->getLastErrorMsg();
			$sock->close();
			$sock = null;
			return false;
		}

		if ($sock->getLastError() != 0) {
			$this->_error_code = $sock->getLastError();
			$this->_error = 'read content error 1'.
					"#code:".strval($sock->getLastError()).
					"#info:".$sock->getLastErrorMsg();
			$sock->close();
			$sock = null;
			return false;
		}

		if (strlen($body) == 0) {
			$this->_error_code = $sock->getLastError();
			$this->_error = 'read content error 2, server close'.
					"#code:".strval($sock->getLastError()).
					"#info:".$sock->getLastErrorMsg();
			$sock->close();
			$sock = null;
			return false;
		}
		$successcount++;
	}

	//	echo "totaltime=".$totaltime."\n";
	//	echo "speed=".$totaltime/$count."\n";
	//	echo "totalsuccess=".$successcount."\n";
		$this->_error_code = 0;
		$this->_error = "";
		return $header_pack.$body;
	}
}

class TcpClientLong
{
	private $sock = null;
	private $host = "127.0.0.1";
	private $port = 9000;
	private $unix = false;
	private $_callTimeout = 0.1;	//second
	private $_connTimeout = 0.01;	//usecond
	private $_error_code = 0;
	private $_error;
	private $_splitter;

	function __construct($splitter) {
		echo "create long";
		$this->_splitter = $splitter;
	}

	public function getLastErrorCode() {
		return $this->_error_code;
	}

	public function getLastError() {
		return $this->_error;
	}

	function setServer($host, $port, $unix = false) {
		$this->host = $host;
		$this->port = $port;
		$this->unix = $unix;
		$this->maintainConnection();
	}

	function setTimeout($conn_timeout = 0.01, $call_timeout = 0.1) {
		if($call_timeout < $conn_timeout) {
			$call_timeout = $conn_timeout;
		}
		$this->_connTimeout = $conn_timeout;
		$this->_callTimeout = $call_timeout;
	}

	private function maintainConnection() {
		if ($this->sock != null) {
			$this->_error_code = 0;
			return true;
		}

		$domain = ($this->unix) ? (AF_UNIX) : (AF_INET);
		$protocol = ($this->unix) ? (0) : (SOL_TCP);
		$this->sock = new CMLibTcpSocket($domain, $protocol);
		$this->sock->clearError();
		$con = $this->sock->connect($this->host, $this->port, $this->_connTimeout);
		if ($con === false) {
			$this->_error_code = $this->sock->getLastError();
			$this->_error = 'connect timeout'.
					"#code:".strval($this->sock->getLastError()).
					"#info:".$this->sock->getLastErrorMsg();
			$this->sock->close();
			$this->sock = null;
			return false;
		}

		$this->_error_code = 0;
		return true;
	}
	
	function send($packet) {
		if (false === $this->maintainConnection())
			return false;
		
		$timeout = new CMLibTimeout($this->_callTimeout);
		$len = $this->sock->write($packet, strlen($packet), $timeout);
		if(false === $len) {
			$this->_error = 'write error'.
					"#code:".strval($this->sock->getLastError()).
					"#info:".$this->sock->getLastErrorMsg();
			$this->sock->close();
			$this->sock = null;
			return false;
		}
		
		return true;
	}
	
	function sendReceive($packet,$count=1) {
		echo "send long";
	
		if (false === $this->maintainConnection()) {
			echo "maintainconnection fail";
			return false;
		}
		echo "start to send";
		$timeout = new CMLibTimeout($this->_callTimeout);
		$count = 1;
		$totalcount = $count;
		while($count-- > 0) {
			$time_start = microtime_float();
			$len = $this->sock->write($packet, strlen($packet), $timeout);
			if ($len === false) {
				$this->_error_code = $this->sock->getLastError();
				$this->_error = 'write error'.
					"#code:".strval($this->sock->getLastError()).
					"#info:".$this->sock->getLastErrorMsg();
				$this->sock->close();
				$this->sock = null;
				return false;
			}

			$header_len = $this->_splitter->getHeaderSize();
			$header_pack = $this->sock->read($header_len, $timeout);
			if ($header_pack === false) {
				$this->_error_code = $this->sock->getLastError();
				$this->_error = 'read header error 0'.
					"#code:".strval($this->sock->getLastError()).
					"#info:".$this->sock->getLastErrorMsg();
				$this->sock->close();
				$this->sock = null;
				return false;
			}
			$time_end = microtime_float();

	                echo "spendtime:".$time_end-$time_start."\n";
                	$totaltime += $time_end-$time_start;
			if ($this->sock->getLastError() != 0) {
				$this->_error_code = $this->sock->getLastError();
				$this->_error = 'read header error 1'.
					"#code:".strval($this->sock->getLastError()).
					"#info:".$this->sock->getLastErrorMsg();
				$this->sock->close();
				$this->sock = null;
				return false;
			}

			if (strlen($header_pack) == 0) {
				$this->_error_code = $this->sock->getLastError();
				$this->_error = 'read header error 2, server close'.
					"#code:".strval($this->sock->getLastError()).
					"#info:".$this->sock->getLastErrorMsg();
				$this->sock->close();
				$this->sock = null;
				return false;
			}

			$len = $this->_splitter->parsePackageSize($header_pack);
			if ($len === false) {
				$this->_error_code = $this->sock->getLastError();
				$this->_error = 'parse head error'.
					"#code:".strval($this->sock->getLastError()).
					"#info:".$this->sock->getLastErrorMsg();
				$this->sock->close();
				$this->sock = null;
				return false;
			}

			$body = $this->sock->read($len - $header_len, $timeout);
			if ($body === false) {
				$this->_error_code = $this->sock->getLastError();
				$this->_error = 'read content error 0'.
					"#code:".strval($this->sock->getLastError()).
					"#info:".$this->sock->getLastErrorMsg();
				$this->sock->close();
				$this->sock = null;
				return false;
			}

			if ($this->sock->getLastError() != 0) {
				$this->_error_code = $this->sock->getLastError();
				$this->_error = 'read content error 1'.
					"#code:".strval($this->sock->getLastError()).
					"#info:".$this->sock->getLastErrorMsg();
				$this->sock->close();
				$this->sock = null;
				return false;
			}

			if (strlen($body) == 0) {
				$this->_error_code = $this->sock->getLastError();
				$this->_error = 'read content error 2, server close'.
					"#code:".strval($this->sock->getLastError()).
					"#info:".$this->sock->getLastErrorMsg();
				$this->sock->close();
				$this->sock = null;
				return false;
			}
			$successcount++;
		}

               // echo "totaltime=".$totaltime."\n";
               // echo "speed=".$totaltime/$totalcount."\n";
               // echo "totalsuccess=".$successcount."\n";	
			$this->_error_code = 0;
			$this->_error = "";
			return $header_pack.$body;
	}

}

} //__TCPCLIENT_PHP_INCLUDED__

?>
